﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.IO;
using System.Threading;

namespace IpodMusicGetter
{
    public partial class IpodMusicGetter : Form
    {
        private static String folderdestination;
        private static String ipodsource;
        private String[] sourceDirectories;
        private static double dsprogress;
        //private int ds2progress;
        public IpodMusicGetter()
        {
            dsprogress = 0;
            InitializeComponent();            
            ts.Start();
            //progressbarTimer.Start();
        }

        private void ts_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (sourceDirectories != null)
                progressBar.Value = (int)((dsprogress / sourceDirectories.Length) * 100);
        }

        private void PickDestination_Click(object sender, EventArgs e)
        {
            GetDestination.ShowDialog();
            folderdestination = GetDestination.SelectedPath;
        }
        private void iPodFiles_Click(object sender, EventArgs e)
        {
            GetiPod.ShowDialog();
            ipodsource = GetiPod.SelectedPath;
        }

        private void GetMusic_Click(object sender, EventArgs e)
        {
            System.GC.Collect();
            sourceDirectories = Directory.GetDirectories(ipodsource + @"\music");
            String[] directorySet1 = new String[sourceDirectories.Length / 2]; //43, then it is 21
            for (int x = 0; x < directorySet1.Length; x++)
            {
                directorySet1[x] = sourceDirectories[x];
            }
            String[] directorySet2 = new String[sourceDirectories.Length - directorySet1.Length]; //if 21 and 43, then it is 22
            for (int x = 0; x < directorySet2.Length; x++)
            {
                directorySet2[x] = sourceDirectories[x + directorySet1.Length];
            }

            Thread ds1 = new Thread(new ParameterizedThreadStart(GetFilesAndConvert));
            ds1.Start(directorySet1);
            Thread ds2 = new Thread(new ParameterizedThreadStart(GetFilesAndConvert));
            ds2.Start(directorySet2);

            //GetFilesAndConvert(sourceDirectories);

        }
    

        private void GetFilesAndConvert(object sourceDirectories)
        {
            foreach (String dir in (String[])sourceDirectories)
            {
                String[] files = Directory.GetFiles(dir);


                ConvertToProperName(files);
                dsprogress++;
            }
        }

        private void ConvertToProperName(String[] files)
        {
            foreach (String file in files)
            {
                if (file.EndsWith("m4p"))
                {
                    M4PTag tag = ReadTagFromFile(file);
                    File.Copy(file, folderdestination + @"\" + tag.Artist + " - " + tag.Title + ".m4p", true);
                }
                else if (file.EndsWith("m4a"))
                {
                    M4PTag tag = ReadTagFromFile(file);
                    File.Copy(file, folderdestination + @"\" + tag.Artist + " - " + tag.Title + ".m4a", true);
                }
                else if (file.EndsWith("mp3") || file.EndsWith("Mp3") || file.EndsWith("MP3"))
                {
                    FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read);

                    //using (BinaryReader br = new BinaryReader(fs))
                    StreamReader sr = new StreamReader(fs, Encoding.Default);

                    String info = sr.ReadToEnd();
                    if (info.Contains("TIT2") && (info.Contains("TPE2") || info.Contains("TPE1")))
                    {
                        try
                        {

                            //LogBytes(file, info);
                            ID3Tag tag = ReadID3v22TagFromInfo(info);

                            File.Copy(file, folderdestination + @"\" + tag.Artist + " - " + tag.Title + ".mp3", true);
                            sr.Dispose();
                            fs.Dispose();
                            continue;
                        }
                        catch (Exception) { Logger(file); }
                    }
                    if (info.Contains("TP1") && info.Contains("TT2"))
                    {
                        try
                        {
                            //LogBytes(file, info);
                            ID3Tag tag = ReadID3v2TagFromInfo(info);

                            File.Copy(file, folderdestination + @"\" + tag.Artist + " - " + tag.Title + ".mp3", true);
                            sr.Dispose();
                            fs.Dispose();
                            continue;
                        }
                        catch (Exception) { Logger(file); }
                    }
                    if (info.Contains("TAG+"))
                    {
                        try
                        {
                            //LogBytes(file, info);
                            ID3Tag tag = ID3EBytesToID3EStruct(ReadID3EFromFileToByte(file));

                            File.Copy(file, folderdestination + @"\" + tag.Artist + " - " + tag.Title + ".mp3", true);
                            sr.Dispose();
                            fs.Dispose();
                            continue;
                        }
                        catch (Exception) { Logger(file); }

                    }
                    if (info.Contains("TAG"))
                    {
                        try
                        {
                            //LogBytes(file, info);
                            ID3Tag tag = ID3BytesToID3Struct(ReadID3FromFileToByte(file));

                            File.Copy(file, folderdestination + @"\" + tag.Artist + " - " + tag.Title + ".mp3", true);
                            sr.Dispose();
                            fs.Dispose();
                            continue;
                        }
                        catch (Exception) { Logger(file); }
                    }
                    sr.Dispose();
                    fs.Dispose();
                }
                else
                {
                    Logger(file);
                }
                //dsprogress++;
            }
        }

        private static void Logger(String file)
        {
            FileStream f = new FileStream(folderdestination + @"\log.txt", FileMode.Append, FileAccess.Write);
            StreamWriter sw = new StreamWriter(f);
            sw.WriteLine("please check: " + file + " to make sure it was correctly renamed");
            sw.Dispose();
            f.Dispose();
        }

        private void LogBytes(String file, String info)
        {
            FileStream f = new FileStream(folderdestination + @"\" + file.Substring(file.Length - 9, 5) + ".txt", FileMode.Create, FileAccess.Write);
            StreamWriter sw = new StreamWriter(f);
            sw.WriteLine(info);
            sw.Dispose();
            f.Dispose();
        }


        private M4PTag ReadTagFromFile(String File)
        {
            M4PTag tag = new M4PTag();
            //FileStream f = new FileStream(folderdestination + @"\" + File.Substring(File.Length - 5,5) +".txt", FileMode.Create, FileAccess.Write);
            //StreamWriter sw = new StreamWriter(f);
            using (FileStream fs = new FileStream(File, FileMode.Open, FileAccess.Read))
            {
                //using (BinaryReader br = new BinaryReader(fs))
                using(StreamReader sr = new StreamReader(fs,Encoding.Default))
                {                   
                    String info = sr.ReadToEnd();
                    
                    int titleindex = info.IndexOf("©nam");
                    int artistindex = info.IndexOf("©ART");
                    tag.Title = info.Substring(titleindex + 16, 200).Split(new String[1] { "\0\0\0" }, StringSplitOptions.RemoveEmptyEntries)[0].TrimStart(new Char[1] { '\0' }).Replace("*", "").Replace("?", "").Replace(":", " ");
                    tag.Artist = info.Substring(artistindex + 16, 200).Split(new String[1] { "\0\0\0" }, StringSplitOptions.RemoveEmptyEntries)[0].TrimStart(new Char[1] { '\0' }).Replace("*", "").Replace("?", "").Replace(":", " ");
                }
            }
            //sw.Dispose();
            //f.Dispose();
            return tag;            
        }
        private ID3Tag ReadID3v22TagFromInfo(String info)
        {
            ID3Tag tag = new ID3Tag();
            int tit2index = info.IndexOf("TIT2"); //track
            
            int tpe2index = info.IndexOf("TPE2"); //artist
            int tpe1index = info.IndexOf("TPE1");
            if (tpe2index == -1 || tpe1index < tpe2index)
                tpe2index = tpe1index;

            String[] atoms = new String[] { "TRCK", "TALB", "TPE1", "TPE2", "TIT2", "COMM", "TCOM", "TCOP", "TCON", "TPE3", "TYER", "TPUB", "PRIV", "TENC", "TPOS", "TLEN", "MCDI" };
            int[] indexes = new int[atoms.Length];

            String bulkyTrack = info.Substring(tit2index, 75);
            int length = bulkyTrack.Length;
            for (int x = 0; x < atoms.Length; x++)
            {
                indexes[x] = bulkyTrack.IndexOf(atoms[x]);
            }
            for (int y = 0; y < indexes.Length; y++)
            {            
                if (indexes[y] < length && indexes[y] != -1 && indexes[y] != 0)
                    length = indexes[y];
                //    int tmp = bulkyTrack.Length - indexes[y];
                //    if (tmp < length && tmp > 0)
                //        length = tmp;
                //}
            }
            bulkyTrack = bulkyTrack.Substring(0, length);
            String[] names = bulkyTrack.Split(new string[1] { "\0\0\0" }, StringSplitOptions.RemoveEmptyEntries);
            for (int x = names.Length - 1; x > -1; x--)
            {
                tag.Title = names[x].Replace("\0", "").Trim().Replace("*", "").Replace("?", "").Replace(":", " ");
                if (tag.Title != "")
                    break;
            }
            //tag.Title = names[names.Length - 1].Replace("\0", "").Trim();

            String bulkyArtist = info.Substring(tpe2index, 75);
            length = bulkyArtist.Length;
            for (int x = 0; x < atoms.Length; x++)
            {
                indexes[x] = bulkyArtist.IndexOf(atoms[x]);
            }
            for (int y = 0; y < indexes.Length; y++)
            {
                if (indexes[y] < length && indexes[y] != -1 && indexes[y] != 0)
                    length = indexes[y];
                //if (indexes[y] != -1)
                //{
                //    //if (bulkyArtist.Length - indexes[y] < length)
                //    //    length = bulkyArtist.Length - indexes[y];
                //    int tmp = bulkyArtist.Length - indexes[y];
                //    if (tmp < length && tmp > 0)
                //        length = tmp;
                //}
            }
            bulkyArtist = bulkyArtist.Substring(0, length);
            names = bulkyArtist.Split(new string[1] { "\0\0\0" }, StringSplitOptions.RemoveEmptyEntries);
            for(int x = names.Length - 1; x > -1; x--)
            {
                tag.Artist = names[x].Replace("\0", "").Trim().Replace("*", "").Replace("?", "").Replace(":", " ");
                if (tag.Artist != "")
                    break;
            }

            return tag;
        }
        private ID3Tag ReadID3v2TagFromInfo(String info)
        {
            ID3Tag tag = new ID3Tag();

            int titleindex = info.IndexOf("TT2");
            int artistindex = info.IndexOf("TP1");
            tag.Title = info.Substring(titleindex + 7, 100).Split(new String[1] { "\0\0" }, StringSplitOptions.RemoveEmptyEntries)[0];
            tag.Title = tag.Title.Substring(0, tag.Title.Length - 4).Replace("*", "").Replace("?", "").Replace(":", " ");
            tag.Artist = info.Substring(artistindex + 7, 100).Split(new String[1] { "\0\0" }, StringSplitOptions.RemoveEmptyEntries)[0];
            tag.Artist = tag.Artist.Substring(0, tag.Artist.Length - 4).Replace("*", "").Replace("?", "").Replace(":", " ");

            return tag;
        }
        private byte[] ReadID3FromFileToByte(string sLocalFile)
        {
            using (FileStream oFS = new FileStream(sLocalFile, FileMode.Open, FileAccess.Read))
            {
                using (BinaryReader oBR = new BinaryReader(oFS))
                {
                    oBR.BaseStream.Position = (oFS.Length - 128);
                    return oBR.ReadBytes((int)oFS.Length);
                }
            }
        }
        private byte[] ReadID3EFromFileToByte(string sLocalFile)
        {
            using (FileStream oFS = new FileStream(sLocalFile, FileMode.Open, FileAccess.Read))
            {
                using (BinaryReader oBR = new BinaryReader(oFS))
                {
                    oBR.BaseStream.Position = (oFS.Length - 227);
                    return oBR.ReadBytes((int)oFS.Length);
                }
            }
        }
        private ID3Tag ID3BytesToID3Struct(byte[] bRawID3)
        {
            GCHandle hRawID3 = GCHandle.Alloc(bRawID3, GCHandleType.Pinned);
            ID3TagByte tag = (ID3TagByte)Marshal.PtrToStructure((hRawID3.AddrOfPinnedObject()), typeof(ID3TagByte));
            hRawID3.Free();

            Char[] artistname = new Char[tag.bArtistsName.Length];
            Char[] trackname = new Char[tag.bTrackName.Length];
            int x = 0;
            foreach (Byte b in tag.bArtistsName)
            {
                artistname[x] = Convert.ToChar(b);
                x++;
            }
            x = 0;
            foreach (Byte b in tag.bTrackName)
            {
                trackname[x] = Convert.ToChar(b);
                x++;
            }
            ID3Tag returntag = new ID3Tag();
            returntag.Artist = new String(artistname).Replace("\0", "").Trim().Replace("*", "").Replace("?", "").Replace(":", " ");
            returntag.Title = new String(trackname).Replace("\0", "").Trim().Replace("*", "").Replace("?", "").Replace(":", " ");

            return returntag;
        }
        private ID3Tag ID3EBytesToID3EStruct(byte[] bRawID3E)
        {
            GCHandle hRawID3 = GCHandle.Alloc(bRawID3E, GCHandleType.Pinned);
            ID3ETagByte tag = (ID3ETagByte)Marshal.PtrToStructure((hRawID3.AddrOfPinnedObject()), typeof(ID3ETagByte));
            hRawID3.Free();

            Char[] artistname = new Char[tag.bArtistsName.Length];
            Char[] trackname = new Char[tag.bTrackName.Length];
            int x = 0;
            foreach (Byte b in tag.bArtistsName)
            {
                artistname[x] = Convert.ToChar(b);
                x++;
            }
            x = 0;
            foreach (Byte b in tag.bTrackName)
            {
                trackname[x] = Convert.ToChar(b);
                x++;
            }

            ID3Tag returntag = new ID3Tag();
            returntag.Artist = new String(artistname).Replace("\0", "").Trim().Replace("*", "").Replace("?", "").Replace(":", " ");
            returntag.Title = new String(trackname).Replace("\0", "").Trim().Replace("*", "").Replace("?", "").Replace(":", " ");

            return returntag;
        }

        private struct M4PTag
        {
            public String Title;
            public String Artist;
        }
        private struct ID3Tag
        {
            public String Title;
            public String Artist;           
        }
        private struct ID3TagByte
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
            public byte[] bTagHeader; //0-2
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)]
            public byte[] bTrackName; //3-32
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)]
            public byte[] bArtistsName; //33-62
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)]
            public byte[] bAlbumName; //63-92
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
            public byte[] bYear; //93-96
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)]
            public byte[] bComment; //97-126
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
            public byte[] bGenres; //127
        }
        private struct ID3ETagByte
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
            public byte[] bTagHeader; 
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 60)]
            public byte[] bTrackName; 
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 60)]
            public byte[] bArtistsName; 
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 60)]
            public byte[] bAlbumName; 
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
            public byte[] bSpeed; 
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)]
            public byte[] bGenres; 
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
            public byte[] bStart; 
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
            public byte[] bEnd; 
        }
    }
}
